home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-04-14 | 16.8 KB | 627 lines | [TEXT/KAHL] |
- /******************************************************************************
- CPaintPane.c
-
- The PaintPane Class
-
- A MacPaint-type pane which stores a bitmap image. The PaintPane has
- its own offscreen image and port. The "real" image is stored offscreen
- and CopyBits operations are performed to put the image on the screen.
- A single ScratchPad BitMap, the same size as the paint image, is used
- in order to have smooth drawing (no flashing). This one scratchpad is
- shared by all PaintPanes (i.e, multiple windows). To implement undo,
- another bitmap is created to store sufficient information to restore
- the state of a painting. Each PaintPane has it's own undo buffer.
- With multiple windows, this means that the "Undo" command will undo
- the last action for the active window.
-
- The drawing tools are pretty simple: geometric shapes, pencil, brush,
- text tool, and selection rectangle.
-
- Geometric shapes are constrained to squares (or circles) if the
- shift key is held down.
-
-
- SUPERCLASS = CBitMapPane
-
- Copyright © 1989 Symantec Corporation. All rights reserved.
-
- ******************************************************************************/
-
- #include "Global.h"
- #include "Commands.h"
- #include "CBartender.h"
- #include "CClipboard.h"
- #include "CPaintPane.h"
- #include "CPaintTask.h"
- #include "CToolShapes.h"
- #include "CToolPens.h"
- #include "CDragger.h"
- #include "CSelectionRect.h"
- #include "CToolText.h"
- #include "CCaption.h" // TCL 2.0 2/14/94 KI
- #include "CClearTask.h"
- #include "ArtClassCmds.h"
- #include "CPNTGFile.h"
-
- /*** Global Variables ***/
-
- extern CBartender *gBartender; /* Manages all menus */
- extern CClipboard *gClipboard; /* Copies and Pastes data */
- extern CBureaucrat *gGopher; /* First in line to get commands */
- extern RgnHandle gUtilRgn; /* Utility region */
- extern long gSleepTime; /* Max time between events */
-
- extern short gFontNum; /* Font number */
- extern short gFontSize; /* Font size */
- extern Style gFontStyle; /* Current font style */
- extern long gLineSpacing; /* Line spacing cmd number */
-
- extern short gPaintTool;
- extern CursHandle gShapeCurs;
- extern CursHandle gPencilCurs;
- extern CursHandle gEraserCurs;
- extern CursHandle gBrushCurs;
- extern CursHandle gDragCurs;
- extern CursHandle gSelectCurs;
- extern CursHandle gIBeamCursor;
- extern CBitMap *gScratchPad;
-
- #define DELETE_KEY 8 /* Character code */
- #define PAT_MARCH_ANTS 300 /* Pattern for marching ants */
- #define iCUT 2 /* Index of task name for Cut */
- #define iCLEAR 5 /* Index of task name for Clear */
-
- /**** C O N S T R U C T I O N / D E S T R U C T I O N M E T H O D S ****/
-
-
- /******************************************************************************
- IPaintPane
-
- Initialize a PaintPane object
- ******************************************************************************/
-
- CPaintPane::CPaintPane(
- CView *anEnclosure,
- CBureaucrat *aSupervisor,
- short aWidth,
- short aHeight,
- short aHEncl,
- short aVEncl,
- SizingOption aHSizing,
- SizingOption aVSizing,
- LongRect *aBounds,
- CBitMap *aBitMap)
- : CBitMapPane(anEnclosure, aSupervisor,
- aWidth, aHeight, aHEncl, aVEncl, aHSizing, aVSizing,
- aBounds, aBitMap, TRUE)
- {
- wantsClicks = TRUE;
- SetLongRect(&selRect, 0, 0, 0, 0);
- itsCaption = NULL; // TCL 2.0 2/14/94 KI
- }
-
- void CPaintPane::StartCaption(LongPt *startPt)
- {
- /* Create a Caption Pane to handle */
- /* keystrokes and text display */
-
- TextFont(gFontNum); /* Set font characteristics */
- TextSize(gFontSize);
- TextFace(gFontStyle);
- /* Note how the PaintPane is both */
- /* the enclosure and supervisor */
- /* of the Caption. This is */
- /* important because we want the */
- /* PaintPane to become the Gopher */
- /* again when we get rid of the */
- /* Caption pane.
- */
- itsCaption = new CCaption(this,this,PNTG_WIDTH,startPt->h,startPt->v);
-
- itsCaption->SetSpacingCmd(gLineSpacing);
- itsCaption->Activate();
- itsCaption->BecomeGopher( TRUE);
- }
- /******************************************************************************
- DoCommand {OVERRIDE}
-
- Handle Edit commands
- ******************************************************************************/
-
- void CPaintPane::DoCommand(
- long theCommand)
- {
- if (HiShort(-theCommand) == MENUtools) {
- KillSelRect();
- CBitMapPane::DoCommand(theCommand);
-
- } else {
- switch (theCommand) {
-
- case cmdCut:
- DoCopy();
- DoClear(iCUT);
- break;
-
- case cmdCopy:
- DoCopy();
- break;
-
- case cmdPaste:
- DoPaste();
- break;
-
- case cmdClear:
- DoClear(iCLEAR);
- break;
-
- default:
- CBitMapPane::DoCommand(theCommand);
- break;
- }
- }
- }
-
-
- /******************************************************************************
- UpdateMenus {OVERRIDE}
-
- Enable appropriate Edit commands
- ******************************************************************************/
-
- void CPaintPane::UpdateMenus()
- {
- CBitMapPane::UpdateMenus();
-
- if (gGopher == this) {
- if (!EmptyLongRect(&selRect)) {
- gBartender->EnableCmd(cmdCut);
- gBartender->EnableCmd(cmdCopy);
- gBartender->EnableCmd(cmdClear);
- }
-
- if (gClipboard->DataSize('PICT') > 0) {
- gBartender->EnableCmd(cmdPaste);
- }
- }
- }
-
-
- /******************************************************************************
- DoClick {OVERRIDE}
-
- Mouse click inside the PaintPane
- ******************************************************************************/
-
- void CPaintPane::DoClick(
- Point hitPt,
- short modifierKeys,
- long when)
- {
- CPaintTask *thePaintTask;
- Rect bounds;
- Boolean select = FALSE;
- Boolean notifiable = TRUE;
- LongPt trackStart;
-
- LongToQDRect( &this->bounds, &bounds);
- QDToLongPt( hitPt, &trackStart);
-
- if (!PtInRect(hitPt, &bounds)) {
- return;
- }
-
- if (gPaintTool != toolSELECT) { /* Any new painting will clobber the */
- KillSelRect(); /* selection */
- }
-
- switch (gPaintTool) {
-
- case toolSELECT: /* We could be selecting or dragging */
- notifiable = FALSE;
- if (PtInLongRect( &trackStart, &selRect)) {
- /* Dragging the current selection */
- if ((itsLastTask == NULL) || !member(itsLastTask, CDragger)) {
- /* This is a brand new drag, NOT the */
- /* continuation of a previous one */
- itsLastTask = new CDragger(this, itsBitMap);
- notifiable = TRUE;
- }
-
- thePaintTask = (CPaintTask*) itsLastTask;
-
- } else { /* Make a selection rectangle */
- select = TRUE;
- KillSelRect();
- thePaintTask = new CSelectionRect(this,itsBitMap);
- gSleepTime = 0; /* Cursor shape needs to change within */
- /* selection, so we must force an */
- /* idle so that the mouse region will */
- /* be updated */
- }
- break;
-
- case toolBRUSH:
- thePaintTask = new CToolBrush(this, itsBitMap);
- break;
-
- case toolPENCIL:
- thePaintTask = new CToolPencil(this, itsBitMap);
- break;
-
- case toolERASER:
- thePaintTask = new CToolEraser(this, itsBitMap);
- break;
-
- case toolRECT:
- thePaintTask = new CToolRect(this, itsBitMap);
- break;
-
- case toolFILLRECT:
- thePaintTask = new CToolFillRect(this, itsBitMap);
- break;
-
- case toolTEXT:
- notifiable = FALSE;
- thePaintTask = new CToolText(this, itsBitMap);
- break;
-
- case toolRRECT:
- thePaintTask = new CToolRRect(this, itsBitMap);
- break;
-
- case toolFILLRRECT:
- thePaintTask = new CToolFillRRect(this, itsBitMap);
- break;
-
- case toolLINE:
- thePaintTask = new CToolLine(this, itsBitMap);
- break;
-
- case toolOVAL:
- thePaintTask = new CToolOval(this, itsBitMap);
- break;
-
- case toolFILLOVAL:
- thePaintTask = new CToolFillOval(this, itsBitMap);
- break;
- }
-
- /* Mouse down drawing within the */
- /* bounds of the painting */
- ForceNextPrepare();
- Prepare();
- TrackMouse(thePaintTask, &trackStart, &this->bounds);
-
- if (select) { /* Selection is not undoable so we */
- delete thePaintTask; /* don't need the task anymore */
- } else {
- if (notifiable) { /* Make sure we don't re-notify in */
- /* the case of a resumed drag */
- itsSupervisor->Notify(thePaintTask);
- }
- itsLastTask = thePaintTask; /* Save task for later reference */
- }
- }
-
-
- /******************************************************************************
- AdjustCursor {OVERRIDE}
-
- Adjust the shape of the cursor according the position of the mouse.
- ******************************************************************************/
-
- void CPaintPane::AdjustCursor(
- Point where, /* Mouse location in Window coords */
- RgnHandle mouseRgn) /* Mouse region in Global coords */
- {
- Rect mouseRect;
- LongPt mouseLoc;
-
- WindToFrame(where, &mouseLoc);
- if (!PtInLongRect( &mouseLoc, &this->bounds)) {
- SetCursor(&qd.arrow); /* Mouse is outside the drawing */
- /* area */
- FrameToGlobalR( &bounds, &mouseRect);
- RectRgn(gUtilRgn, &mouseRect);
- DiffRgn(mouseRgn, gUtilRgn, mouseRgn);
- return;
- }
-
- switch (gPaintTool) { /* Cursor shape depends on tool */
-
- case toolSELECT:
- if (PtInLongRect( &mouseLoc, &selRect)) {
- SetCursor(*gDragCurs);
- } else {
- SetCursor(*gSelectCurs);
- }
- break;
-
- case toolBRUSH:
- SetCursor(*gBrushCurs);
- break;
-
- case toolPENCIL:
- SetCursor(*gPencilCurs);
- break;
-
- case toolERASER:
- SetCursor(*gEraserCurs);
- break;
-
- case toolTEXT:
- SetCursor(*gIBeamCursor);
- break;
-
- case toolRECT:
- case toolFILLRECT:
- case toolRRECT:
- case toolFILLRRECT:
- case toolLINE:
- case toolOVAL:
- case toolFILLOVAL:
- SetCursor(*gShapeCurs);
- break;
- }
-
- if (!EmptyLongRect(&selRect)) { /* Cursor changes shape inside */
- /* selection rectangle */
- FrameToGlobalR( &selRect, &mouseRect);
- RectRgn(gUtilRgn, &mouseRect);
- if (PtInLongRect( &mouseLoc, &selRect)) {
- SectRgn(mouseRgn, gUtilRgn, mouseRgn);
- } else {
- DiffRgn(mouseRgn, gUtilRgn, mouseRgn);
- }
- }
- /* In case frame is bigger than */
- FrameToGlobalR( &bounds, &mouseRect); /* bounds */
- RectRgn(gUtilRgn, &mouseRect);
- SectRgn(mouseRgn, gUtilRgn, mouseRgn);
- }
-
-
- /******************************************************************************
- DoKeyDown {OVERRIDE}
-
- Hitting the backspace/delete key clears the current selection
- ******************************************************************************/
-
- void CPaintPane::DoKeyDown(
- char theChar,
- Byte keyCode,
- EventRecord *macEvent)
- {
- if ((theChar == DELETE_KEY) && !EmptyLongRect(&selRect)) {
- DoClear(iCLEAR);
- }
- else
- CBitMapPane::DoKeyDown(theChar, keyCode, macEvent);
- }
-
-
- /******************************************************************************
- Dawdle {OVERRIDE}
-
- Animate the selection rectangle during Idle time
- ******************************************************************************/
-
- void CPaintPane::Dawdle(
- long *maxSleep)
- {
- Pattern thePat;
- long ticks;
- Rect sRect;
-
- if (!EmptyLongRect(&selRect)) { /* Make sure selection exists */
- LongToQDRect( &selRect, &sRect);
- Prepare();
-
- PenNormal(); /* Draw rectangle with one pattern */
- #ifndef TCL_UNIVERSAL_HEADERS // KMI 3/10/94
- GetIndPattern(thePat, PAT_MARCH_ANTS, 1);
- #else
- GetIndPattern(&thePat, PAT_MARCH_ANTS, 1);
- #endif
- PenPat(&thePat);
- FrameRect(&sRect);
-
- Delay(6, &ticks); /* Wait a while */
-
- /* Redraw with a pattern offset from */
- /* the first. This gives the */
- /* illusion of marching ants. */
- #ifndef TCL_UNIVERSAL_HEADERS // KMI 3/10/94
- GetIndPattern(thePat, PAT_MARCH_ANTS, 2);
- #else
- GetIndPattern(&thePat, PAT_MARCH_ANTS, 2);
- #endif
- PenPat(&thePat);
- FrameRect(&sRect);
- *maxSleep = 5;
- }
- }
-
-
- /******************************************************************************
- ChangeSize
-
- Change the size of a PaintPane. If necessary, shift painting so
- that it always completely covers the frame.
- ******************************************************************************/
-
- void CPaintPane::ChangeSize(
- register Rect *delta, /* Movement for each side */
- Boolean redraw) /* Redraw Pane or not? */
- {
- short hShift;
- short vShift;
-
- /* If new size would cause the */
- /* frame to extend beyond the */
- /* bounds of the painting, we */
- /* have to shift the painting. */
- hShift = Max(frame.right + delta->right - bounds.right, 0);
- vShift = Max(frame.bottom + delta->bottom - bounds.bottom, 0);
-
- CBitMapPane::ChangeSize(delta, redraw);
-
- if ( (hShift > 0) || (vShift > 0) ) {
- Scroll(-hShift, -vShift, false);
- if (redraw) {
- Refresh();
- }
- }
- }
-
-
- /******************************************************************************
- KillSelRect
-
- Get rid of selection rectangle and finalize the last task
- ******************************************************************************/
-
- void CPaintPane::KillSelRect()
- {
- Rect sRect;
-
- if (itsLastTask != NULL) { /* Killing selection means we are about */
- itsLastTask->Do(); /* to do something new. Let the last */
- Notify( NULL); /* task finalize its actions. */
- itsLastTask = NULL;
- }
- if (itsCaption != NULL)
- {
- PaintCaption(); // TCL 2.0 2/16/94
- NotifyClean(NULL);
- }
- FrameToWindR( &selRect, &sRect);
- /* Set empty selection and force redraw */
- SetLongRect(&selRect, 0, 0, 0, 0); /* of old selection to clear out the */
- DrawAll(&sRect); /* marching ants */
- }
-
-
-
- /******************************************************************************
- DoCopy
-
- Copy selection to the clipboard. This action is NOT undoable.
- ******************************************************************************/
-
- void CPaintPane::DoCopy()
- {
- PicHandle thePic;
- LongRect selRect;
- Rect qdRect;
-
- Prepare();
- itsBitMap->BeginDrawing();
-
- FrameToQDR( &this->selRect, &qdRect);
- /* Open picture to store selection */
- thePic = OpenPicture( &qdRect);
- /* Draw selection on top of itself */
- selRect = this->selRect;
- itsBitMap->CopyFrom( &selRect, &selRect, NULL);
- ClosePicture();
- itsBitMap->EndDrawing();
- /* Copy picture to the clipboard */
- gClipboard->EmptyScrap();
- gClipboard->PutData('PICT', (Handle)thePic);
- KillPicture(thePic);
- }
-
-
- /******************************************************************************
- DoPaste
-
- Paste picture from the clipboard. This action isn't truly
- undoable. We fool the pane into thinking that a drag has
- occurred.
- ******************************************************************************/
-
- void CPaintPane::DoPaste()
- {
- PicHandle thePic;
- LongPt anyPt;
- Rect pFrame;
-
- if (gClipboard->GetData('PICT', (Handle*) &thePic)) {
- /* Set tool to selection rectangle */
- /* so pasted image can be dragged */
- DoCommand(-( (((long)MENUtools) << 16) + toolSELECT));
- /* Paste is treated as a drag in */
- itsLastTask = new CDragger(this, itsBitMap);
-
- /* Place pasted picture near the */
- /* top left of the pane */
- QDToLongRect( &(**thePic).picFrame, &selRect);
- OffsetLongRect(&selRect, frame.left + 36 - selRect.left,
- frame.top + 36 - selRect.top);
-
- /* Save bits under the paste */
- LongToQDRect( &selRect, &pFrame);
- CopyBits(itsBitMap->macBitMap, gScratchPad->macBitMap,
- &pFrame, &pFrame, srcCopy, NULL);
-
- itsBitMap->BeginDrawing(); /* Draw pasted image on painting */
- DrawPicture(thePic, &pFrame);
- itsBitMap->EndDrawing();
-
- anyPt = topLeftL( selRect); /* Fake out Dragger into thinking */
- /* a selection has been dragged */
- ((CDragger*)itsLastTask)->BeginTracking( &anyPt);
-
- CopyBits(gScratchPad->macBitMap, itsBitMap->macBitMap,
- &pFrame, &pFrame, srcCopy, NULL);
-
- ((CDragger*)itsLastTask)->EndTracking(&anyPt, &anyPt, &anyPt);
-
- RefreshRect(&pFrame); /* Force redraw and proceed as */
- gSleepTime = 0; /* a suspended drag */
- itsSupervisor->Notify(itsLastTask);
- KillPicture(thePic);
- }
- }
-
-
- /******************************************************************************
- DoClear
-
- Erase the current selection
- ******************************************************************************/
-
- void CPaintPane::DoClear(
- short taskIndex)
- {
- register CClearTask *theClear;
- /* Can't get much simpler: */
- /* Create a task, do it, and tell */
- /* supervisor it's been done */
- theClear = new CClearTask(taskIndex, this, itsBitMap);
- theClear->Do();
- itsSupervisor->Notify(theClear);
- itsLastTask = theClear;
- }
-
- /******************************************************************************
- PaintCaption TCL 2.0 2/16/94 KI
-
- Finish off Text
- ******************************************************************************/
-
- void CPaintPane::PaintCaption()
- {
- LongRect tFrame;
-
- itsCaption->Deactivate();
- itsCaption->FitToText ();
- itsCaption->DrawInPort(itsBitMap->macPort);
-
- delete itsCaption;
- itsCaption = NULL;
-
- BecomeGopher(TRUE);
- }